/**************************************************************************************
*
*	Copyright (C) 2009 - 2012 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	EffectsLibrary.cpp
*
*	Comments	-	See EffectsLibrary.h
*
**************************************************************************************/
#include "../Include/EffectsLibrary.h"
#include "../Include/XMLParser.h"
#include "../Include/GlobalDirectory.h"
#include "../Include/String.h"
#include "../Include/GraphicsResourceManager.h"

namespace Pulse
{
	EffectsLibrary::EffectsLibrary( void )
	{

	}

	EffectsLibrary::~EffectsLibrary( void )
	{
		Cleanup();
	}

	BOOL EffectsLibrary::Load( const CHAR *pEffectsLibrary, const CHAR *pRenderTechLibrary )
	{
		if ( !LoadEffectsLibrary( pEffectsLibrary ) || !LoadRenderTechniquesLibaray( pRenderTechLibrary ) )
		{
			PSX_PushWarning( "Failed to load render effects and render techniques library." );
			return FALSE;
		}

		RegisterTechniquesToEffects();

		// All okay
		return TRUE;
	}

	BOOL EffectsLibrary::LoadEffectsLibrary( const CHAR *pLibraryFile )
	{
		if ( pLibraryFile == PSX_NULL )
		{
			PSX_PushWarning( "No filename specified to load effects library." );
			return FALSE;
		}

		String realPath = String(PSX_PATH_EFFECTS_LIBRARY) + pLibraryFile;
		XMLDocumentPtr pDoc = XMLParser::Load( realPath.GetBuffer() );

		if( pDoc.IsValid() == FALSE )
		{
			PSX_PushWarning( "Failed to load library file." );
			return FALSE;
		}

		XMLElementPtr pElem = pDoc->FindChild( PSX_String("EffectsLibrary") );

		if ( pElem.IsNull() )
		{
			PSX_PushWarning( "No EffectsLibrary tag found. Loading aborted." );
			return FALSE;
		}

		XMLElementPtr pChild = pElem->FindChild( PSX_String("Effects") );

		if ( pChild.IsNull() )
		{
			PSX_PushWarning( "Failed to find effects info." );
			//return EErrorCode::INVALID_FORMAT;
			return FALSE;
		}

		if ( ProcessEffects( pChild ) != EErrorCode::OKAY )
		{
			//return EErrorCode::_ERROR;
			return FALSE;
		}

		return TRUE;
	}

	BOOL EffectsLibrary::LoadRenderTechniquesLibaray( const CHAR *pLibraryFile )
	{
		if ( pLibraryFile == PSX_NULL )
		{
			PSX_PushWarning( "No filename specified to load effects library." );
			return FALSE;
		}

		String realPath = String(PSX_PATH_EFFECT_TECHNIQUES) + pLibraryFile;
		XMLDocumentPtr pDoc = XMLParser::Load( realPath.GetBuffer() );

		if( pDoc.IsValid() == FALSE )
		{
			PSX_PushWarning( "Failed to load library file." );
			return FALSE;
		}

		XMLElementPtr pElem = pDoc->FindChild( PSX_String("RenderTechniquesLibrary") );

		if ( pElem.IsNull() )
		{
			PSX_PushWarning( "No RenderTechniquesLibrary tag found. Loading aborted." );
			return FALSE;
		}

		XMLElementPtr pChild = pElem->FindChild( PSX_String("RenderTechniques") );

		if ( pChild.IsNull() )
		{
			PSX_PushWarning( "Failed to find render techniques info." );
			//return EErrorCode::INVALID_FORMAT;
			return FALSE;
		}

		if ( ProcessRenderTechniques( pChild ) != EErrorCode::OKAY )
		{
			//return EErrorCode::_ERROR;
			return FALSE;
		}

		return TRUE;
	}

	void EffectsLibrary::Cleanup( void )
	{
		m_effectsMap.Clear();

		for ( SIZE_T i = 0; i < m_effects.GetSize(); ++i )
		{
			m_effects[i]->Release();
		}

		m_effects.Clear();

		for ( SIZE_T i = 0; i < m_renderTechnqiues.GetSize(); ++i )
		{
			m_renderTechnqiues[i]->Release();
		}

		m_renderTechnqiues.Clear();
	}

	void EffectsLibrary::RegisterTechniquesToEffects( void )
	{
		SIZE_T numTechniques = m_renderTechnqiues.GetSize();

		for ( SIZE_T i = 0; i < numTechniques; ++i )
		{
			RenderTechnique *pTech = m_renderTechnqiues[i];

			SIZE_T numEffectRegisters = pTech->GetNumEffectRegisters();
			EffectRegister *pRegisters = pTech->GetEffectRegisters();
			EffectRegister *pRegister = PSX_NULL;
			
			for ( SIZE_T j = 0; j < numEffectRegisters; ++j )
			{
				pRegister = pRegisters + j;

				auto findIter = m_effectsMap.Find( pRegister->effectID );

				if( findIter != m_effectsMap.IteratorEnd() )
				{
					findIter->second->RegisterTechnique( pTech, pRegister->priority );
				}
				else
				{
					PSX_PushWarning( "The effect specified in the effect register doesn't exist." );
				}
			}
		}
	}

	EErrorCode::Type EffectsLibrary::ProcessEffects( XMLElementPtr pElem )
	{
		GraphicsResourceManager *pGM = GraphicsResourceManager::GetInstance();
		XMLElementPtr pChild = pElem->GetFirstChild();

		while ( pChild.IsValid() )
		{
			if ( pChild->GetName() == PSX_String("Effect") )
			{
				Effect *pNewEffect;
				EffectDesc desc;
				String effectFile = pChild->FindChild(PSX_String("File"))->GetValue(); 
				String ID = pChild->FindChild(PSX_String("ID"))->GetValue(); 
				//String sortID = pChild->GetAttribute( PSX_String("SortID") ); 

				desc.SetToLoad( effectFile.GetBuffer() );

				pNewEffect = pGM->CreateEffect( effectFile.GetBuffer(), &desc );
			
				if ( pNewEffect )
				{
					EffectID id = ID.ToUINT16();

					auto findIter = m_effectsMap.Find( id );
				
					if ( findIter == m_effectsMap.IteratorEnd() )
					{
						m_effects.PushBack( pNewEffect );
						m_effectsMap[id] = pNewEffect;
					}
					else
					{
						PSX_PushError( "Effect id already taken by a previously loaded effect." );
					}
				}
				else
				{
					PSX_PushWarning( "Failed to load effect file." );
				}
			}

			pChild = pChild->GetNextSibling();
		}

		return EErrorCode::OKAY;
	}

	EErrorCode::Type EffectsLibrary::ProcessRenderTechniques( XMLElementPtr pElem )
	{
		GraphicsResourceManager *pGM = GraphicsResourceManager::GetInstance();
		XMLElementPtr pChild = pElem->GetFirstChild();

		while ( pChild.IsValid() )
		{
			if ( pChild->GetName() == PSX_String("Element") )
			{
				RenderTechnique *pNewTech;
				RenderTechniqueDesc desc;
				String techFile = pChild->FindChild( PSX_String("Technique"))->GetValue();

				desc.SetToLoad( techFile.GetBuffer() );

				pNewTech = pGM->CreateRenderTechnique( techFile.GetBuffer(), &desc );
			
				if ( pNewTech )
				{
					// Before adding the technique we need to get the effect registers first
					{
						XMLElementPtr pEffectRegisters = pChild->FindChild( PSX_String("EffectRegisters") );
						XMLElementPtr pChild = pEffectRegisters->GetFirstChild();

						while ( pChild.IsValid() )
						{
							// Process each register
							if ( pChild->GetName() == PSX_String("Element") )
							{
								EffectRegister newRegister;

								String buffer = pChild->FindChild(PSX_String("EffectID"))->GetValue();
								newRegister.effectID = buffer.ToUINT16();
								buffer = pChild->FindChild(PSX_String("Priority"))->GetValue();
								newRegister.priority= (TechniquePriority)buffer.ToUINT8();

								pNewTech->AddEffectRegister( &newRegister );
							}
							pChild = pChild->GetNextSibling();
						}
					}

					m_renderTechnqiues.PushBack( pNewTech );
				}
				else
				{
					PSX_PushWarning( "Failed to load render technique file." );
				}
			}

			pChild = pChild->GetNextSibling();
		}

		return EErrorCode::OKAY;
	}
}